package checker

import (
	"strings"
	"unicode/utf8"
	"kumachan/interpreter/lang/ast"
	"kumachan/interpreter/compiler/checker/checked"
	"kumachan/interpreter/compiler/checker/typsys"
)


func checkString(S ast.StringLiteral) ExprChecker {
	return makeExprChecker(S.Location, func(cc *checkContext) checkResult {
		var buf strings.Builder
		writeAllRunes(&buf, S.First.Value)
		for _, part := range S.Parts {
			switch P := part.Part.(type) {
			case ast.StringText:
				writeAllRunes(&buf, P.Value)
			case ast.CharLiteral:
				var r, err = parseRune(P)
				if err != nil { return cc.propagate(err) }
				buf.WriteRune(r)
			default:
				panic("impossible branch")
			}
		}
		var value = buf.String()
		return cc.assign(
			cc.getType(coreString),
			checked.StringLiteral { Value: value })
	})
}

func checkFormatter(F ast.Formatter) ExprChecker {
	return makeExprChecker(F.Location, func(cc *checkContext) checkResult {
		var explicit_char = make(map[uint] bool)
		var buf strings.Builder
		writeAllRunes(&buf, F.First.Template)
		for _, part := range F.Parts {
			switch P := part.Part.(type) {
			case ast.FormatterText:
				writeAllRunes(&buf, P.Template)
			case ast.CharLiteral:
				var r, err = parseRune(P)
				if err != nil { return cc.propagate(err) }
				explicit_char[uint(buf.Len())] = true
				buf.WriteRune(r)
			default:
				panic("impossible branch")
			}
		}
		var template = buf.String()
		var parameters = make([] uint, 0)
		{ var i = uint(0)
		for _, char := range template {
			if char == StringFormatterPlaceholder && !(explicit_char[i]) {
				parameters = append(parameters, i)
			}
			i += uint(utf8.RuneLen(char))
		} }
		if len(parameters) == 0 {
			return cc.assign(
				cc.getType(coreString),
				checked.StringLiteral { Value: template })
		} else {
			var string_type = cc.getType(coreString)
			var input_type = (func() typsys.Type {
				if len(parameters) == 1 {
					return string_type
				} else {
					var parameter_types = make([] typsys.Type, len(parameters))
					for i := range parameters {
						parameter_types[i] = string_type
					}
					return &typsys.NestedType { Content:
						typsys.Tuple {
							Elements: parameter_types,
						} }
				}
			})()
			return cc.assign(
				&typsys.NestedType { Content: typsys.Lambda {
					Input:  input_type,
					Output: string_type,
				} },
				checked.StringFormatter {
					Template:   template,
					Parameters: parameters,
				})
		}
	})
}

func writeAllRunes(buf *strings.Builder, runes ([] rune)) {
	for _, r := range runes {
		buf.WriteRune(r)
	}
}


